#ifndef __MM_H
#define __MM_H

#include <type.h>
#include <mm/pgtable.h>
#include <mm/memregion.h>
#include <ds/list.h>

#define PAGE_MAX_SIZE 1024 * 1024

// 用来获取特定的页表的虚拟地址
#define TBL_VADDR(pd_offset) (TBL_BASE_VADDR | (pd_offset << 12))

#define DIRECTORY_INDEX(vaddr) (u32)(((uptr)(vaddr)&0xFFC00000UL) >> 22)
#define TABLE_INDEX(vaddr) (u32)(((uptr)(vaddr)&0x003FF000UL) >> 12)
#define PG_OFFSET(vaddr) (u32)((uptr)(vaddr)&0x00000FFFUL)

#define PG_PRESENT          1
#define PG_WRITE            2
#define PG_ALLOW_USER       4
#define PG_WRITE_THROUGH    8
#define PG_DISABLE_CACHE    16
#define PG_LRU_LAST_CHANCE  (0x1 << 11) // the flags bits in page_t only 12 bits

#define VMAP_NULL       0
#define VMAP_IGNORE     1 /* Ignore if mapping exists */
#define VMAP_RESERVE    2 /* Reserve entry without mapping */
#define VMAP_NON_RAM    4 /* Non-RAM memory */

#define PG_LOCKED 1
#define PG_KERNEL 2

#define PDE_ENTRY(mnt, pn) \
    (((pg_table_t*)(mnt | 0x003FF000))->entry[(pn)>>10])

#define PT_BASE(mnt, pn) \
    ((pg_table_t*)((mnt) | (((pn)&0xffc00) << 2)))

#define PTE_ENTRY(mnt, pn) \
    (((pg_table_t*)((mnt) | (((pn)&0xffc00) << 2)))->entry[(pn)&0x3ff])

// physical memory page number start
#define PM_PN_START 1

// physical memory page
typedef struct page {
    u32 ref_counts;
    struct {
        u32 index : 20;
        u32 flags : 12;
    };
    lnode_t lru;
    uptr* mapping;
} page_t;

void mm_init(u32 end);

int free_page(uptr addr);

int ref_page(uptr addr);

uptr get_page(u32 flags);

uptr get_pages(size_t count, u32 flags);

uptr get_page_zero(u32 flags);

void mark_page_free(uptr pn);

void mark_chunk_free(uptr start_pn, size_t count);

void mark_page_used(uptr pn, u32 flags);

void mark_chunk_used(uptr start_pn, size_t count, u32 flags);

page_t* page_query(uptr addr);

void set_page_unlock(page_t* page);

void set_page_addr_unlock(uptr pa);

uptr page_query_pn(page_t* page);

int mapping_p2v(uptr va, uptr pa, u32 flags, u32 opt);

uptr mapping_pages(uptr pa, size_t count, u32 flags, u32 opt);

uptr unmapping_p2v(uptr va);

uptr copy_page(uptr pa);

void mapping_page_dir(uptr mnt, uptr pde);

void unmapping_page_dir(uptr mnt);

void pm_usage();

uptr get_pa(uptr va);

#endif  // __MM_H